using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
using System.Linq;
using RVO;
using Unity.Mathematics;
using System;
using UnityEngine.Profiling;


public class ARPGTestSceneMgr : BaseScene
{
    SCamerContrller camerContrller;
    ARPGTestPlayerBehaviour player;
    public ARPGTestPlayerBehaviour Player => player;
    public Collider[] checkMonsters;
    Light light;
    LinkedListNode<ARPGTestBoarBehaviour> boarsLinkedList;
    int mapSize = 2;

    Transform canvas3D;

    public ARPGTestMainPanel mainPanel;
    // Start is called before the first frame update
    void Start()
    {
        AssetBundleManager abmgr = AssetBundleManager.Instance;
        checkMonsters = new Collider[10];

        mainPanel = UIManager.Instance.OpenPlane<ARPGTestMainPanel>();

        ObjectPoolManager.Instance.InitBundles(abmgr.bundles[abmgr.POOL_BUNDLE], abmgr.bundles);

        player = ObjectPoolManager.Instance.DePool<ARPGTestPlayerBehaviour>("ARPGTest_PlayerA");

        SoundManager.Instance.Init();

        canvas3D = UIManager.Instance.GetCanvas(CanvasType.ThreeD);

        InitLight();
        InitCamera();   
        InitGround();
        InitSim();
    }


    Dictionary<int, float2> goals;
    private readonly System.Random random = new System.Random(0);

    private Simulator simulator;
    private List<int> agents;
    private List<DynamicObstacleData> obstacles;
    void InitSim()
    {
        simulator = new Simulator();
        obstacles = new List<DynamicObstacleData>();
        agents = new List<int>();
      
        SetupScenario();
        StartCoroutine(this.Simulator());
    }

    
    public int AddAgent(Transform transform) {
        float2 p = new float2(transform.position.x,transform.position.z);
        var agentId = this.simulator.AddAgent(p);
        this.agents.Add(agentId);
        return agentId;
    }

    public Vector3 GetAgentPos(int id) {
        float2 p = simulator.GetAgentPosition(id);
        return new Vector3(p.x,0,p.y);
    }

    public void RemoveAgent(int id) {
        if (simulator.RemoveAgent(id))
        {
            agents.Remove(id);
        }
    }

    private IEnumerator Simulator()
    {
        while (true)
        {
            //this.sampler.Begin();
            SetPreferredVelocities(PlayerPosUpdate());
            simulator.DoStep();
            simulator.EnsureCompleted();
           
            //this.sampler.End();
            yield return null;
        }
    }

    float2 playerPos;
    float2 PlayerPosUpdate() {
        playerPos.x = player.transform.position.x;
        playerPos.y = player.transform.position.z;
        return playerPos;
    }

    void ApplyMode() { 
    
    }

    void SetupScenario()
    {
        // Specify the global time step of the simulation.
        this.simulator.SetTimeStep(0.25f);

        // Specify the default parameters for agents that are subsequently added.
        this.simulator.SetAgentDefaults(15f, 10, 5f, 5f, 1f, 0.05f, new float2(0f, 0f));

        // Add agents, specifying their start position.
        this.agents = new List<int>();

        AddObstacles();
    }

    void AddObstacles() {
        //for (var i = 0; i < 5; ++i)
        //{
        //    for (var j = 0; j < 5; ++j)
        //    {
        //        var agentId = this.simulator.AddAgent(new float2(55f + (i * 10f), 55f + (j * 10f)));
        //        this.agents.Add(agentId);

        //        agentId = this.simulator.AddAgent(new float2(-55f - (i * 10f), 55f + (j * 10f)));
        //        this.agents.Add(agentId);

        //        agentId = this.simulator.AddAgent(new float2(55f + (i * 10f), -55f - (j * 10f)));
        //        this.agents.Add(agentId);

        //        agentId = this.simulator.AddAgent(new float2(-55f - (i * 10f), -55f - (j * 10f)));
        //        this.agents.Add(agentId);
        //    }
        //}

        //this.obstacles = new List<DynamicObstacleData>();

        //// Add (polygonal) obstacles, specifying their vertices in counterclockwise order.
        //IList<float2> obstacle1 = new List<float2>
        //    {
        //        new float2(-10f, 40f),
        //        new float2(-40f, 40f),
        //        new float2(-40f, 10f),

        //        // new float2(-10f, 10f),
        //        new float2(-30f, 10f),
        //        new float2(-30f, 30f),
        //        new float2(-10f, 30f),
        //    };
        //var obstacle1Id = this.simulator.AddObstacle(obstacle1);

        //IList<float2> obstacle2 = new List<float2>
        //    {
        //        new float2(10f, 40f),

        //        // new float2(10f, 10f),
        //        new float2(10f, 30f),
        //        new float2(30f, 30f),
        //        new float2(30f, 10f),

        //        new float2(40f, 10f),
        //        new float2(40f, 40f),
        //    };
        //var obstacle2Id = this.simulator.AddObstacle(obstacle2);

        //IList<float2> obstacle3 = new List<float2>
        //    {
        //        new float2(10f, -40f),
        //        new float2(40f, -40f),
        //        new float2(40f, -10f),

        //        // new float2(10f, -10f),
        //        new float2(30f, -10f),
        //        new float2(30f, -30f),
        //        new float2(10f, -30f),
        //    };
        //var obstacle3Id = this.simulator.AddObstacle(obstacle3);

        //IList<float2> obstacle4 = new List<float2>
        //    {
        //        new float2(-10f, -40f),

        //        // new float2(-10f, -10f),
        //        new float2(-10f, -30f),
        //        new float2(-30f, -30f),
        //        new float2(-30f, -10f),

        //        new float2(-40f, -10f),
        //        new float2(-40f, -40f),
        //    };
        //var obstacle4Id = this.simulator.AddObstacle(obstacle4);

        //this.obstacles.Add(new DynamicObstacleData { points = obstacle1, active = true, obstacleId = obstacle1Id });
        //this.obstacles.Add(new DynamicObstacleData { points = obstacle2, active = true, obstacleId = obstacle2Id });
        //this.obstacles.Add(new DynamicObstacleData { points = obstacle3, active = true, obstacleId = obstacle3Id });
        //this.obstacles.Add(new DynamicObstacleData { points = obstacle4, active = true, obstacleId = obstacle4Id });

        //IList<float2> boundVerts = new List<float2>
        //    {
        //        new float2(-100f, 100f),
        //        new float2(100f, 100f),
        //        new float2(100f, -100f),
        //        new float2(-100f, -100f),
        //    };
        //this.bounds = this.simulator.AddObstacle(boundVerts);
    }

    private void SetPreferredVelocities(float2 newGoal)
    {
        foreach (var agentId in this.agents)
        {
            float2 goalVector = newGoal - this.simulator.GetAgentPosition(agentId);

            if (math.lengthsq(goalVector) > 1f)
            {
                goalVector = math.normalize(goalVector);
            }

            this.simulator.SetAgentPrefVelocity(agentId, goalVector);

            // Perturb a little to avoid deadlocks due to perfect symmetry.
            var angle = (float)this.random.NextDouble() * 2f * (float)Math.PI;
            var dist = (float)this.random.NextDouble() * 0.0001f;

            this.simulator.SetAgentPrefVelocity(
                agentId,
                this.simulator.GetAgentPrefVelocity(agentId) + (dist * new float2((float)Math.Cos(angle), (float)Math.Sin(angle))));
        }
    }
    private void SetPreferredVelocities()
    {
        // Set the preferred velocity to be a vector of unit magnitude
        // (speed) in the direction of the goal.
        foreach (var pair in this.goals)
        {
            var agentId = pair.Key;
            var goal = pair.Value;
            float2 goalVector = goal - this.simulator.GetAgentPosition(agentId);

            if (math.lengthsq(goalVector) > 1f)
            {
                goalVector = math.normalize(goalVector);
            }

            this.simulator.SetAgentPrefVelocity(agentId, goalVector);
        }
    }

    private bool ReachedGoal()
    {
        // Check if all agents have reached their goals.
        foreach (var pair in this.goals)
        {
            var agentId = pair.Key;
            var goal = pair.Value;
            if (math.lengthsq(
                this.simulator.GetAgentPosition(agentId) - goal)
                    > this.simulator.GetAgentRadius(agentId) * this.simulator.GetAgentRadius(agentId))
            {
                return false;
            }
        }
        return true;
    }

    Dictionary<Vector2Int, ARPGTestGroundBehaviour> grounds;
    [SerializeField]
    Vector2Int groundID = Vector2Int.zero;
    Vector2Int preGroundID = Vector2Int.zero;

    void InitGround() {
        grounds = new Dictionary<Vector2Int, ARPGTestGroundBehaviour>();
        GetGroundID();
        List<Vector2Int> groundIDs = new List<Vector2Int>();
        for (int i = groundID.x - mapSize; i <= groundID.x + mapSize; i++)
        {
            for (int j = groundID.y - mapSize; j <= groundID.y + mapSize; j++)
            {
                groundIDs.Add(new Vector2Int(i, j));
            }
        }
        foreach (var item in groundIDs)
        {
            if (!grounds.ContainsKey(item))
            {
                ARPGTestGroundBehaviour g = ObjectPoolManager.Instance.DePool<ARPGTestGroundBehaviour>("GroundA");
                g.InitID(item);
                grounds.Add(item, g);
            }
        }
        preGroundID = groundID;
    }

    void GetGroundID() {
        groundID.x = (int)(player.transform.position.x * 0.1f);
        groundID.y = (int)(player.transform.position.z * 0.1f);
    }

    void RereshGroundID() {
        List<Vector2Int> preGourndIDs = new List<Vector2Int>();
        List<Vector2Int> groundIDs = new List<Vector2Int>();

        for (int i = preGroundID.x - mapSize; i <= preGroundID.x + mapSize; i++)
        {
            for (int j = preGroundID.y - mapSize; j <= preGroundID.y + mapSize; j++)
            {
                preGourndIDs.Add(new Vector2Int(i, j));
            }
        }
        for (int i = groundID.x- mapSize; i <= groundID.x + mapSize; i++)
        {
            for (int j = groundID.y- mapSize; j <= groundID.y+ mapSize; j++)
            {
                groundIDs.Add(new Vector2Int(i, j));
            }
        }
        List<Vector2Int> commonElements = preGourndIDs.Intersect(groundIDs).ToList();
        preGourndIDs.RemoveAll(item => commonElements.Contains(item));
        groundIDs.RemoveAll(item => commonElements.Contains(item));
        foreach (var item in preGourndIDs)
        {
            if (grounds.ContainsKey(item))
            {
                ObjectPoolManager.Instance.EnPool(grounds[item]);
                grounds.Remove(item);
            }
        }
        foreach (var item in groundIDs)
        {
            if (!grounds.ContainsKey(item))
            {
                ARPGTestGroundBehaviour g = ObjectPoolManager.Instance.DePool<ARPGTestGroundBehaviour>("GroundA");
                g.InitID(item);
                grounds.Add(item, g);
            }
        }

        preGroundID = groundID;
    }

    void GroundIDUpdate() {
        GetGroundID();
        if (groundID!=preGroundID)
        {
            RereshGroundID();
        }
    }

    void InitLight()
    {
        light = GameObject.FindObjectOfType<Light>();

        light.intensity = 0.5f;
    }

    void InitCamera()
    {
        Destroy(Camera.main.gameObject);
        camerContrller = ObjectPoolManager.Instance.DePool<SCamerContrller>("SCamerContrller", true, "CommonPre");
        camerContrller.SetBackGround(false);
        camerContrller.SetRotation(new Vector3(-45, 45, 0));
        camerContrller.gameObject.AddComponent<Fllower>().target = player.transform;
        camerContrller.SetOP(true);
        camerContrller.SetSize(4.5f);
    }

    Vector2 inputDir;
    void Update()
    {
        inputDir.x = Input.GetAxis("Horizontal");
        inputDir.y = Input.GetAxis("Vertical");

        player.SetInputDir(inputDir);

        GroundIDUpdate();
    }

    private void OnDestroy()
    {
        StopAllCoroutines();
        this.simulator.Clear();
        this.simulator.Dispose();
    }

    void Damage() { 
    
    }
}

struct DynamicObstacleData
{
    public IList<float2> points;
    public bool active;
    public int obstacleId;
}
public class Fllower:MonoBehaviour{
    public  Transform target;
    public Vector3 offset;
    public void Init(Transform target,Vector3 offset) {
        this.target = target;
        this.offset = offset;
    }

    private void Update()
    {
        transform.position = target.position+offset;
    }
}
